﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Nop.Services.Payments;
using Nop.Core.Plugins;
using Nop.Services.Configuration;
using Nop.Services.Tax;
using Nop.Services.Catalog;
using Nop.Services.Directory;
using Nop.Services.Customers;
using Nop.Core.Domain.Directory;
using Nop.Core;
using Nop.Core.Domain;
using System.Web.Routing;
using Nop.Plugin.Payments.PagSeguro.Controllers;
using Nop.Core.Domain.Payments;
using System.Text;
using System.Globalization;
using Nop.Services.Orders;
using System.Net;
using System.IO;
using System.Xml;
using System.ComponentModel;
using Uol.PagSeguro;

namespace Nop.Plugin.Payments.PagSeguro
{
    public class PagSeguroPaymentProcessor : BasePlugin, IPaymentMethod
    {
        #region Fields
        private readonly PagSeguroPaymentSettings _pagSeguroPaymentSettings;
        private readonly ISettingService _settingService;
        private readonly ITaxService _taxService;
        private readonly IPriceCalculationService _priceCalculationService;
        private readonly ICurrencyService _currencyService;
        private readonly ICustomerService _customerService;
        private readonly CurrencySettings _currencySettings;
        private readonly IWebHelper _webHelper;
        private readonly StoreInformationSettings _storeInformationSettings;
        #endregion

        #region Ctor
        public PagSeguroPaymentProcessor(PagSeguroPaymentSettings pagSeguroPaymentSettings,
            ISettingService settingService,
            ITaxService taxService, IPriceCalculationService priceCalculationService,
            ICurrencyService currencyService, ICustomerService customerService,
            CurrencySettings currencySettings, IWebHelper webHelper,
            StoreInformationSettings storeInformationSettings)
        {
            this._pagSeguroPaymentSettings = pagSeguroPaymentSettings;
            this._settingService = settingService;
            this._taxService = taxService;
            this._priceCalculationService = priceCalculationService;
            this._currencyService = currencyService;
            this._customerService = customerService;
            this._currencySettings = currencySettings;
            this._webHelper = webHelper;
            this._storeInformationSettings = storeInformationSettings;
        }
        #endregion

        #region Default Methods
        /// <summary>
        /// Processa pagamento
        /// </summary>
        /// <param name="processPaymentRequest"></param>
        /// <returns></returns>
        public ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest)
        {
            var result = new ProcessPaymentResult();
            result.NewPaymentStatus = PaymentStatus.Pending;
            return result;
        }
        
        /// <summary>
        /// PostProcessPayment utilizando API de pagamento do PagSeguro
        /// </summary>
        /// <param name="postProcessPaymentRequest"></param>
        public void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest)
        {
            string returnToken = string.Empty;
            AccountCredentials credentials = new AccountCredentials(_pagSeguroPaymentSettings.Email, _pagSeguroPaymentSettings.Token);
            try
            {
                PaymentRequest payment = new PaymentRequest();
                
                //Esse campo sempre deve ser BRL. PagSeguro não trabalha com outras moedas
                payment.Currency = "BRL";

                string tipoFrete = string.Empty;
                payment.Shipping = new Shipping();

                if (!string.IsNullOrEmpty(postProcessPaymentRequest.Order.ShippingMethod))
                {
                    switch (postProcessPaymentRequest.Order.ShippingMethod)
                    {
                        case "PAC":
                            tipoFrete = "PAC";
                            payment.Shipping.ShippingType = ShippingType.Pac;
                            break;
                        case "SEDEX":
                            tipoFrete = "SEDEX";
                            payment.Shipping.ShippingType = ShippingType.Sedex;
                            break;
                        case "SEDEX 10":
                            tipoFrete = "SEDEX 10";
                            payment.Shipping.ShippingType = ShippingType.Sedex;
                            break;
                        default:
                            payment.Shipping.ShippingType = ShippingType.NotSpecified;
                            break;
                    }
                }

                //Pega produtos no carrinho de compras
                decimal totalItens = 0;
                var cartItems = postProcessPaymentRequest.Order.OrderProductVariants;
                foreach (var item in cartItems)
                {
                    var product = postProcessPaymentRequest.Order.OrderProductVariants
                                        .Where(x => x.ProductVariantId == item.ProductVariantId)
                                        .FirstOrDefault();

                    if (product == null)
                        return;

                    decimal productPrice = product.UnitPriceInclTax;
                    string productName = (string.IsNullOrEmpty(item.ProductVariant.Name) ? "Nome não especificado" : item.ProductVariant.Name);
                    
                    payment.Items.Add(new Item(item.ProductVariantId.ToString(),
                                               productName,
                                               item.Quantity,
                                               decimal.Parse(productPrice.ToString("0.00", CultureInfo.InvariantCulture)),
                                               (long)item.ProductVariant.Weight));

                    totalItens += (productPrice * item.Quantity);
                }

                //Adicionar produto "Frete" - bacalhau do PagSeguro
                if (postProcessPaymentRequest.Order.ShippingAddressId.HasValue && postProcessPaymentRequest.Order.OrderShippingInclTax > 0)
                {
                    decimal valueZero = 0;
                    payment.Items.Add(new Item("99",
                                                   string.Format("Frete via {0}", tipoFrete),
                                                   1,
                                                   decimal.Parse(postProcessPaymentRequest.Order.OrderShippingInclTax.ToString("0.00", CultureInfo.InvariantCulture)),
                                                   decimal.Parse(valueZero.ToString("0.00", CultureInfo.InvariantCulture))
                                                   ));
                }

                //Campo extra amount usado para os descontos. Deve ser negativo!
                decimal extraAmount = postProcessPaymentRequest.Order.OrderDiscount;
                if (extraAmount > 0)
                    payment.ExtraAmount = decimal.Parse(extraAmount.ToString("0.00", CultureInfo.InvariantCulture));

                //Calcula se há resgate de pontos
                decimal rewardPoints = totalItens
                                        - postProcessPaymentRequest.Order.OrderTotal
                                        - postProcessPaymentRequest.Order.OrderDiscount
                                        + postProcessPaymentRequest.Order.OrderShippingInclTax;
                
                if (postProcessPaymentRequest.Order.OrderTotal < (totalItens + postProcessPaymentRequest.Order.OrderShippingInclTax - postProcessPaymentRequest.Order.OrderDiscount))
                    payment.ExtraAmount += rewardPoints;

                //Transforma em negativo
                if (payment.ExtraAmount.HasValue)
                    payment.ExtraAmount = payment.ExtraAmount * -1;

                //Corrige para duas casas decimais
                if (payment.ExtraAmount.HasValue)
                    payment.ExtraAmount = decimal.Parse(((decimal)payment.ExtraAmount).ToString("0.00", CultureInfo.InvariantCulture));

                //Campo que o transaction ID utilizado futuramente no retorno automático para identificar a compra
                payment.Reference = postProcessPaymentRequest.Order.OrderGuid.ToString();

                if (postProcessPaymentRequest.Order.ShippingAddressId.HasValue && postProcessPaymentRequest.Order.ShippingAddress != null)
                {
                    payment.Shipping.Address = new Address("BRA",
                                                            postProcessPaymentRequest.Order.ShippingAddress.StateProvince.Abbreviation,
                                                            postProcessPaymentRequest.Order.ShippingAddress.City,
                                                            "--",//Bairro no endereço
                                                            postProcessPaymentRequest.Order.ShippingAddress.ZipPostalCode,
                                                            postProcessPaymentRequest.Order.ShippingAddress.Address1,
                                                            "--", //Número do endereço já está no Address1 ^^^^^^^^^
                                                            postProcessPaymentRequest.Order.ShippingAddress.Address2);
                }

                payment.Sender = new Sender(postProcessPaymentRequest.Order.Customer.GetFullName(),
                                                postProcessPaymentRequest.Order.Customer.Email,
                                                new Phone());

                Uri paymentRedirectUri = Uol.PagSeguro.PaymentService.Register(credentials, payment);

                //string redirect = string.Format("<script language=\"javascript\">window.open('{0}','Motokraft - PagSeguro')</script>", paymentRedirectUri.ToString());

                //Ainda não descobri como fazer abrir em pop up!
                if (paymentRedirectUri == null)
                    throw new NopException("Erro ao gerar transação no PagSeguro. Uri nula.");
                else
                    HttpContext.Current.Response.Redirect(paymentRedirectUri.ToString());
            }
            catch (PagSeguroServiceException exception)
            {
                if (exception.StatusCode == HttpStatusCode.Unauthorized)
                {
                    throw new NopException("Unauthorized: please verify if the credentials used in the web service call are correct.\n");
                }
                else
                {
                    string err = "Errors: ";
                    foreach (PagSeguroServiceError error in exception.Errors)
                    {
                        err += error.Message + " ";
                    }
                    throw new NopException(err);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public decimal GetAdditionalHandlingFee()
        {
            return _pagSeguroPaymentSettings.ValorFrete;
        }

        public CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest)
        {
            var result = new CapturePaymentResult();
            result.AddError("Capture method not supported");
            return result;
        }

        public RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest)
        {
            var result = new RefundPaymentResult();
            result.AddError("Refund method not supported");
            return result;
        }

        public VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest)
        {
            var result = new VoidPaymentResult();
            result.AddError("Void method not supported");
            return result;
        }

        public ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentRequest processPaymentRequest)
        {
            var result = new ProcessPaymentResult();
            result.AddError("Recurring payment not supported");
            return result;
        }

        public CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurringPaymentRequest cancelPaymentRequest)
        {
            var result = new CancelRecurringPaymentResult();
            result.AddError("Recurring payment not supported");
            return result;
        }

        public bool CanRePostProcessPayment(Core.Domain.Orders.Order order)
        {
            if (order == null)
                throw new ArgumentNullException("bool CanRePostProcessPayment");

            //payment status should be Pending
            if (order.PaymentStatus != PaymentStatus.Pending)
                return false;

            //let's ensure that at least 1 minute passed after order is placed
            if ((DateTime.UtcNow - order.CreatedOnUtc).TotalMinutes < 1)
                return false;

            return true;
        }

        public void GetConfigurationRoute(out string actionName, out string controllerName, out System.Web.Routing.RouteValueDictionary routeValues)
        {
            actionName = "Configure";
            controllerName = "PaymentPagSeguro";
            routeValues = new RouteValueDictionary() { { "Namespaces", "Nop.Plugin.Payments.PagSeguro.Controllers" }, { "area", null } };
        }

        public void GetPaymentInfoRoute(out string actionName, out string controllerName, out System.Web.Routing.RouteValueDictionary routeValues)
        {
            actionName = "PaymentInfo";
            controllerName = "PaymentPagSeguro";
            routeValues = new RouteValueDictionary() { { "Namespaces", "Nop.Plugin.Payments.PagSeguro.Controllers" }, { "area", null } };
        }
        #endregion

        #region Propriedades
        public Type GetControllerType()
        {
            return typeof(PaymentPagSeguroController);
        }

        public bool SupportCapture
        {
            get
            {
                return false;
            }
        }

        public bool SupportPartiallyRefund
        {
            get
            {
                return false;
            }
        }

        public bool SupportRefund
        {
            get
            {
                return false;
            }
        }

        public bool SupportVoid
        {
            get
            {
                return false;
            }
        }

        public RecurringPaymentType RecurringPaymentType
        {
            get
            {
                return RecurringPaymentType.NotSupported; ;
            }
        }

        public Nop.Services.Payments.PaymentMethodType PaymentMethodType
        {
            get
            {
                return Nop.Services.Payments.PaymentMethodType.Redirection;
            }
        }
        #endregion
    }
}